package ru.fadeev.crammer;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * GFIndexer class is a process that build phrase-word index for Gaps Filling
 * Test. Gaps Filling test task is to match some words and phrases consisting
 * them. To fasten search of needed phrases there is phrase-word index stored
 * in database. When new word or phrase are added, the task created and 
 * stored in GFIndicesStorage. GFIndexer is a background process that gets
 * tasks from GFIndicesStorage, builds phrase-word index and saves it to
 * database
 * @author Konstantin Fadeyev
 *
 */
public class GFIndexer extends Thread {
	private volatile boolean interrupted;
	private GFIndicesStorage storage;
	protected ProgressBar progressBar;
	
	
	/**
	 * Creates instance of GFIndexer with given storage and progress bar
	 * @param storage object of GFIndicesStorage
	 * @param progressBar progress bar
	 */
	public GFIndexer(GFIndicesStorage storage, ProgressBar progressBar) {
		interrupted = false;
		this.storage = storage;
		this.progressBar = progressBar;		
	}
	
	
	/**
	 * SQL query for finding phrase for custom word does very rough search,
	 * that can return a lot of junk. This function is more precise. It checks
	 * if phrase actually contains word.
	 * @param word word
	 * @param phrase phrase
	 * @return true, if phrase contains word
	 */
    private boolean isRightPhrase(String word, String phrase) {
    	int wordsNumber = word.split("[\\s]+").length;
    	if(wordsNumber == 1) {
    		return true;
    	}
    	String regExp = word.trim().replaceAll("[\\s]+", 
    			" (([\\\\w]+)|([\\\\w]+ [\\\\w]+)) ");
    	Pattern p = Pattern.compile(regExp);
    	Matcher matcher = p.matcher(phrase);
    	if(matcher.find()) {
    		return true;
    	}
    	return false;
    }
	
	
	/**
	 * Gets task from GFIndicesStorage, builds phrase-word index and saves
	 * it to database.
	 */
	public void run() {
		try {
			Connection connection = DatabaseManager.getConnection();
			storage.loadGFIndexerTasks();
			while(!interrupted) {
				IndexerTask task = storage.getGFIndexerTask();
				if(task == null) {
					break;
				}
				int tasksSize = storage.getGFIndexerTasksSize();
				String tasksSizeString = String.valueOf(tasksSize);
				if(tasksSize >= 1000) {
					tasksSizeString = "~" + String.valueOf(tasksSize/1000)+ "k";
				} else if (tasksSize >= 100){
					tasksSizeString = "~" + String.valueOf(tasksSize/100)+ "00";
				} else if (tasksSize >= 10) {
					tasksSizeString = "~" + String.valueOf(tasksSize/10)+ "0";					
				}
				
				String note = "building index (" + tasksSizeString + 
								" records remains)...";
				if(!progressBar.isPBVisible()) {
					progressBar.showPBIndeterminate(note);
				} else {
					progressBar.setPBNote(note);
				}
				if(task instanceof IndexerPhraseTask) {
					IndexerPhraseTask phraseTask = (IndexerPhraseTask)task;
					if(!task.inserted) {
						synchronized(storage) {
							storage.deleteGFIndices(
									phraseTask.getPhraseId(), true, 
									connection);
						}
					}
					String phraseText = phraseTask.text;
					
					Statement statement = connection.createStatement();
					ResultSet resultSet = statement.executeQuery(
							"SELECT ID, WORD " +
							"FROM WORD_RECORD " + 
							"WHERE (UPPER( " +
								"(SELECT PHRASE_TEXT " +
								"FROM PHRASE " +
								"WHERE ID = " + phraseTask.getPhraseId() + ")" +
								") LIKE " + 
							"UPPER(CONCAT(CONCAT('% ',REPLACE(WORD,' ','% ')),'%'))) OR " + 
							"(UPPER(" +
								"(SELECT PHRASE_TEXT " +
								"FROM PHRASE " +
								"WHERE ID= " + phraseTask.getPhraseId() + ")" +
								") LIKE " + 
							"UPPER(CONCAT(REPLACE(WORD,' ','% '),'%')))");
					
					while(resultSet.next()) {
						int wordId = resultSet.getInt(1);
			    		String wordText = resultSet.getString(2);
			    		if(isRightPhrase(wordText, phraseText)) {
			    			storage.addGFIndex(wordId, 
			    					phraseTask.getPhraseId(), connection);
			    		}
					}		
					statement.close();
				} else {
					IndexerWordTask wordTask = (IndexerWordTask)task;
					if(!task.inserted) {
						synchronized(storage) {
							storage.deleteGFIndices(wordTask.getWordId(), 
									false, connection);
						}
					}
					
					String word = task.text;
					
					Statement statement = connection.createStatement();
					ResultSet resultSet = statement.executeQuery(
			    			"SELECT ID, PHRASE_TEXT " +
			    			"FROM PHRASE " +
			    			"WHERE (PHRASE_TEXT LIKE '% " + 
			    			word.trim().replaceAll("\\s+", "% ") + "%') OR" +
			    				"(PHRASE_TEXT LIKE '" + 
					    			word.trim().replaceAll("\\s+", "% ") + "%')");
					int phrasesCount = 0;
					while(resultSet.next()) {
						int phraseId = resultSet.getInt(1);
			    		String phraseText = resultSet.getString(2);
			    		if(isRightPhrase(word, phraseText)) {
			    			storage.addGFIndex(wordTask.getWordId(), 
			    					phraseId, connection);
			    			phrasesCount++;
			    		}
					}
					statement.close();
				}
				if(storage.getGFIndexerTasksSize() == 0) {
					progressBar.hidePB();
				}
			}
			connection.close();
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * Overwritten so volatile variable that is used to stop process is initialized.
	 */
    public void start() {
        interrupted = false;
        super.start();
    }
	
	
	/**
	 * Stops indexer process. Function waits till it stops.
	 */
	public void stopIndexer() {
		if(storage.getGFIndexerTasksSize() != 0) {		
			storage.saveGFIndexerTasks();			
		}
		
		interrupted = true;
		interrupt();
		try {
			this.join();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
